-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_12.ex
91 lines (77 loc) · 2.55 KB
/
day_12.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
defmodule AdventOfCode.Day12 do
import AdventOfCode.Util
def part1(args) do
heightMap = getHeightmap(args)
[{stop, _}, {start, _} | _] = heightMap |> Map.to_list() |> List.keysort(1)
# clean start and stop:
heightMap = heightMap |> Map.put(start, 0) |> Map.put(stop, ?z - ?a)
queue = [{start, 0, heuristic(start, stop)}]
updateUntilStop(queue, heightMap, stop)
end
def part2(args) do
heightMap = getHeightmap(args)
[{stop, _}, {start, _} | _] = heightMap |> Map.to_list() |> List.keysort(1)
# clean start and stop:
heightMap = heightMap |> Map.put(start, 0) |> Map.put(stop, ?z - ?a)
{[xMax, _], _} = heightMap |> Enum.max()
startingPoints = 1..xMax |> Enum.map(&[&1, 0])
startingPoints
|> Task.async_stream(&updateUntilStop([{&1, 0, heuristic(start, stop)}], heightMap, stop),
timeout: :infinity
)
|> Enum.min()
end
@spec heuristic([number, ...], [number, ...]) :: number
def heuristic([cx, cy], [gx, gy]) do
abs(gx - cx) + abs(gy - cy)
end
def getHeightmap(args) do
args
|> String.split("\n")
|> Enum.with_index(fn row, rix ->
String.codepoints(row)
|> Enum.with_index(fn <<v::utf8>>, cix -> %{[rix, cix] => v - ?a} end)
end)
|> List.flatten()
|> Enum.reduce(%{}, &Map.merge(&1, &2))
end
def getPossibleMoves(heightMap, currentPosition, step) do
[getN(currentPosition), getS(currentPosition), getW(currentPosition), getE(currentPosition)]
|> Enum.filter(&Map.has_key?(heightMap, &1))
|> Enum.filter(&(Map.fetch!(heightMap, &1) <= 1 + Map.fetch!(heightMap, currentPosition)))
|> Enum.map(&{&1, step + 1})
end
def updateQueue(queue, heightMap, stop) do
posMoves =
queue
|> Enum.map(&getPossibleMoves(heightMap, elem(&1, 0), elem(&1, 1)))
|> Enum.flat_map(& &1)
if List.keymember?(posMoves, stop, 0) do
[{stop, elem(List.keyfind!(posMoves, stop, 0), 1)}]
else
posMoves
|> Enum.reduce(
queue,
fn {move, step}, accQueue ->
if List.keymember?(accQueue, move, 0),
do: accQueue,
else:
List.keystore(
accQueue,
move,
0,
{move, step, heuristic(move, stop)}
)
end
)
end
end
def updateUntilStop(queue, heightMap, stop) do
if List.keymember?(queue, stop, 0) do
elem(List.keyfind(queue, stop, 0), 1)
else
nextQueue = updateQueue(queue, heightMap, stop)
updateUntilStop(nextQueue, heightMap, stop)
end
end
end